package org.apache.ocean.main.search;

import org.apache.lucene.index.Term;
import org.apache.lucene.search.spans.SpanFirstQuery;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanNotQuery;
import org.apache.lucene.search.spans.SpanOrQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.apache.ocean.main.IndexSchema;
import org.apache.ocean.main.client.span.SpanClient;
import org.jdom.Element;

public class SpanQueryParser {
	private IndexSchema.Category schemaCategory;
	private SpanQuery result;
	
	private SpanQueryParser(Element element, IndexSchema.Category schemaCategory) throws Exception {
		this.schemaCategory = schemaCategory;
		SpanClient.SpanQuery clientSpanQuery = SpanClient.getSpanQuery(element);
		result = createSpanQuery(clientSpanQuery);
	}
	
	public SpanQuery getQuery() {
		return result;
	}
	
	public static SpanQuery parse(Element element, IndexSchema.Category schemaCategory) throws Exception {
		SpanQueryParser spanQueryParser = new SpanQueryParser(element, schemaCategory);
		return spanQueryParser.getQuery();
	}
  
	public SpanQuery createSpanQuery(SpanClient.SpanQuery clientSpanQuery) throws Exception {
		if (clientSpanQuery instanceof SpanClient.SpanOrQuery) {
			SpanClient.SpanOrQuery clientSpanOrQuery = (SpanClient.SpanOrQuery) clientSpanQuery;
			return createSpanOrQuery(clientSpanOrQuery);
		} else if (clientSpanQuery instanceof SpanClient.SpanNearQuery) {
			SpanClient.SpanNearQuery clientSpanNearQuery = (SpanClient.SpanNearQuery) clientSpanQuery;
			return createSpanNearQuery(clientSpanNearQuery);
		} else if (clientSpanQuery instanceof SpanClient.SpanFirstQuery) {
			SpanClient.SpanFirstQuery clientSpanFirstQuery = (SpanClient.SpanFirstQuery) clientSpanQuery;
			return createSpanFirstQuery(clientSpanFirstQuery);
		} else if (clientSpanQuery instanceof SpanClient.SpanNotQuery) {
			SpanClient.SpanNotQuery clientSpanNotQuery = (SpanClient.SpanNotQuery) clientSpanQuery;
			return createSpanNotQuery(clientSpanNotQuery);
		} else if (clientSpanQuery instanceof SpanClient.SpanTermQuery) {
			SpanClient.SpanTermQuery clientSpanTermQuery = (SpanClient.SpanTermQuery) clientSpanQuery;
			return createSpanTermQuery(clientSpanTermQuery);
		} else {
			throw new Exception("unknown clientSpanQuery: "+clientSpanQuery.getClass().getName());
		}
	}
	
	public SpanTermQuery createSpanTermQuery(SpanClient.SpanTermQuery clientSpanTermQuery) {
		String field = clientSpanTermQuery.getField();
		String text = clientSpanTermQuery.getText();
		String indexed = schemaCategory.readableToIndexed(field, text);
		return new SpanTermQuery(new Term(field, indexed));
	}
	
	public SpanNotQuery createSpanNotQuery(SpanClient.SpanNotQuery clientSpanNotQuery) throws Exception {
		SpanQuery include = createSpanQuery(clientSpanNotQuery.getInclude());
		SpanQuery exclude = createSpanQuery(clientSpanNotQuery.getExclude());
		return new SpanNotQuery(include, exclude);
	}
	
	public SpanFirstQuery createSpanFirstQuery(SpanClient.SpanFirstQuery clientSpanFirstQuery) throws Exception {
		SpanQuery match = createSpanQuery(clientSpanFirstQuery.getSpanQuery());
		return new SpanFirstQuery(match, clientSpanFirstQuery.getEnd());
	}

	public SpanNearQuery createSpanNearQuery(SpanClient.SpanNearQuery clientSpanNearQuery) throws Exception {
		SpanQuery[] clauses = toSpanQueries(clientSpanNearQuery.getClauses());
		return new SpanNearQuery(clauses, clientSpanNearQuery.getSlop(), clientSpanNearQuery.isInOrder());
	}
  
	public SpanOrQuery createSpanOrQuery(SpanClient.SpanOrQuery clientSpanQuery) throws Exception {
		SpanQuery[] clauses = toSpanQueries(clientSpanQuery.getClauses());
		return new SpanOrQuery(clauses);
	}
	
	public SpanQuery[] toSpanQueries(SpanClient.SpanQuery[] clientClauses) throws Exception {
		SpanQuery[] clauses = new SpanQuery[clientClauses.length];
		for (int x=0; x < clientClauses.length; x++) {
			clauses[x] = createSpanQuery(clientClauses[x]);
		}
		return clauses;
	}
}
